package com.siebre.picclife.asynctask.service.support;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.OverlappingFileLockException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.io.Files;
import com.siebre.picclife.asynctask.TaskException;
import com.siebre.picclife.asynctask.service.TaskRecoverer;
import com.siebre.picclife.asynctask.service.TaskService;

/**
 * 
 * @author ZhangChi
 * @since 2015年4月10日
 *
 */
public class DefaultTaskRecoverer implements TaskRecoverer {
	
	private Logger logger = LoggerFactory.getLogger(getClass());
	
	private boolean recovered = false;
	
	private final Optional<String> previousProcessorId;
	
	private final TaskServiceIdManager idManager;
	
	private final TaskService taskService;

	public DefaultTaskRecoverer(TaskServiceIdManager idManager,
			TaskService taskService) {
		this.idManager = idManager;
		this.taskService = taskService;
		previousProcessorId = idManager.loadId();
	}

	public void recover() {
		if (recovered) {
			return;
		}
		if (previousProcessorId.isPresent()) {
			logger.info("task recovery started");
			int count = taskService.recover(previousProcessorId.get());
			logger.info("task recovery finished, {} tasks are recovered", count);
		}
		idManager.persistId(taskService.getId());
		recovered = true;
	}

	public static class TaskServiceIdManager {
		
		private Logger logger = LoggerFactory.getLogger(getClass());
		
		private File dir;
		private File file;
		
		public TaskServiceIdManager(String path) {
			dir = new File(path);
			if (!dir.exists() && !dir.mkdirs()) {
				throw new TaskException("failed to create directory: " + path);
			}
			try {
				lockFile(0);
			} catch (IOException e) {
				throw new TaskException("failed to generate task service id file under: " + path, e);
			}
		}

		private void lockFile(int index) throws IOException {
			String fileName = "task-processor-id-" + index;
			file = new File(dir, fileName);
			File lockFile = new File(dir, fileName + ".lock");
			lockFile.createNewFile();
			try {
				new FileOutputStream(lockFile).getChannel().tryLock();
			} catch (IOException e) {
				lockFile(index + 1);
			} catch (OverlappingFileLockException e) {
				lockFile(index + 1);
			}
		}

		public Optional<String> loadId() {
			try {
				String id = Files.asCharSource(file, Charsets.UTF_8).read();
				logger.debug("previous task processor id detected: {}", id);
				return Optional.of(id);
			} catch (IOException e) {
				logger.debug("previous task processor id is not detected");
				return Optional.absent();
			}
		}
		
		public void persistId(String id) {
			try {
				Files.asCharSink(file, Charsets.UTF_8).write(id);
			} catch (IOException e) {
				throw new TaskException("failed to persist task service id to file system", e);
			}
		}
		
		public void clear() {
			if (!file.delete()) {
				logger.warn("failed to delete task service id file: " + file);
			}
		}
	}

}
